Гистограмма
является многоугольником, сформированным из последовательности прямоугольников,
выровненных на общей базовой линии. Прямоугольники имеют равную ширину, но
могут иметь различные высоты. Например, фигура слева показывает гистограмму,
которая состоит из прямоугольников с высотами 2, 1, 4, 5, 1, 3, 3. Все
прямоугольники на этом рисунке имеют ширину, равную 1.
Обычно
гистограммы используются для представления дискретных распределений, например,
частоты символов в текстах. Отметьте, что порядок прямоугольников очень важен.
Вычислите область самого большого прямоугольника в гистограмме, который также
находится на общей базовой линии. На рисунке справа заштрихованная фигура
является самым большим выровненным прямоугольником на изображенной гистограмме.
Вход. В первой строке записано число n (0 ≤ n ≤ 106) – количество прямоугольников гистограммы.
Затем следует n целых чисел h1, ..., hn (0 ≤ hi
≤ 109). Эти числа обозначают высоты прямоугольников
гистограммы слева направо. Ширина каждого прямоугольника равна 1.
Выход. Вывести
площадь самого большого прямоугольника в гистограмме. Помните, что этот
прямоугольник должен быть на общей базовой линии.
Пример входа |
Пример выхода |
7 2 1
4 5 1 3 3 |
8 |
структуры
данных - стек
Реализация
за O(n2). Для каждого i-го прямоугольника ширины 1 постараемся раздвинуть его границы
влево left и вправо right пока это возможно (то есть высоты
всех прямоугольников от left-го до right-го не менее hi, 1 ≤ left
≤ i ≤ right ≤ n). Получим максимально возможный прямоугольник, вписанный в
гистограмму, который упирается в верх i-го
прямоугольника. Среди всех таких прямоугольников ищем максимум. Решение дает
Time Limit.
Впишем максимальный
прямоугольник, упирающийся в верх 5-го прямоугольника. Его границами будут [left; right] = [3; 7], высота равна 2. Площадь равна (right – left + 1) * h5 = (7 – 3 + 1) * 2 = 5 * 2 = 10.
Реализация
за O(n). Каждый прямоугольник характеризуется абсциссой i и высотой hi. Заведем стек из пар (i, hi) –
характеристик прямоугольников. Введем в рассмотрение два дополнительных
прямоугольника с высотами h0
= -1, hn+1 = 0.
Занесем нулевой прямоугольник с высотой -1 в стек (пару (0, -1) кладем в стек).
Такие высоты выбраны для того чтобы:
·
нулевой прямоугольник никогда не был
извлечен из стека;
·
обработка последнего прямоугольника с
высотой 0 вытолкнет из стека все имеющиеся там прямоугольники кроме нулевого;
Пусть текущим
является i-ый прямоугольник абсциссой
i и высотой hi. Тогда:
·
Если его высота больше высоты
прямоугольника на вершине стека, то заносим i-ый
прямоугольник в стек.
·
Пока высота текущего прямоугольника (i, hi)
меньше или равна высоте прямоугольника на вершине стека (x, hx) (то
есть hi ≤ hx), то извлекаем прямоугольник
из стека и вычисляем площадь максимального вписанного в гистограмму
прямоугольника. Подозрительным на максимальный будет прямоугольник со сторонами
i – x (он начинается в абсциссе x
и заканчивается в абсциссе i – 1) и hx. Пусть последний
вытолкнутый из стека прямоугольник ширины 1 имеет характеристики (j, hj)
(hj ≥ hi). Тогда в стек следует
положить пару (j, hi).
Пример
Пусть мы дошли
до четвертого прямоугольника включительно. Поскольку до него высоты
прямоугольников шли по возрастающей, то они добавлялись в стек. Следующий пятый
прямоугольник имеет высоту 2. Последовательно извлекаем прямоугольники из
стека, высоты которых строго больше текущего и пересчитываем площади
получившихся максимальных прямоугольников:
Пятый
прямоугольник имеет высоту 2, извлекаем из стека первый прямоугольник,
пересчитываем площадь:
В стеке остался
только нулевой прямоугольник с высотой -1. Последний вытолкнутый из стека
прямоугольник имеет характеристики (j,
hj) = (1, 2). Текущим
рассматриваемым является прямоугольник номер 5 с высотой 2, то есть (i, hi)
= (5, 2). Следовательно в стек заносим прямоугольник с параметрами (j, hi)
= (1, 2). В дальнейшем состояние стека будет следующим:
В структуре Node будем хранить абсциссу x и высоту прямоугольника Height в этой абсциссе. Объявим стек s
из этих структур.
struct Node
{
int x;
int Height;
Node(int x, int Height) : x(x), Height(Height) {};
};
stack<Node>
s;
Читаем входные данные. Считаем высоты нулевого и (n + 1)-го прямоугольника равными соответственно
-1 и 0. Занесем в стек нулевой прямоугольник. Поскольку его высота равна -1, то
он никогда не будет вытолкнут из стека.
scanf("%d",&n);
s.push(Node(0,-1));
Последовательно обрабатываем прямоугольники. Площадь
максимального покрывающего гистограмму прямоугольника подсчитываем в переменной
res.
res
= 0;
for(i
= 1; i <= n + 1; i++)
{
Читаем высоту i-го
прямоугольника. Его абсцисса x равна i. Если i = n + 1, то его высота
равна нулю: в конце необходимо вытолкнуть все прямоугольники из стека кроме
первого с высотой -1 и пересчитать искомую площадь.
if (i <=
n) scanf("%d",&h); else h = 0;
int x = i;
while (h <= s.top().Height)
{
x = s.top().x; hPrev = s.top().Height;
s.pop();
area = 1LL * hPrev * (i - x);
if (area
> res) res = area;
}
s.push(Node(x,h));
}
Выводим площадь наибольшего прямоугольника.
printf("%lld\n",res);
Реализация за O(n2)
#include <stdio.h>
#define MAX 1000010
long long h[MAX];
int
i, n, left, right;
long long area, res;
int
main(void)
{
scanf("%d",&n);
for(i = 1; i
<= n; i++)
scanf("%d",&h[i]);
res = 0;
for(i = 1; i
<= n; i++)
{
left = right = i;
while(left
> 1 && h[left-1] >= h[i]) left--;
while(right
< n && h[right+1] >= h[i]) right++;
area = (right - left + 1) * h[i];
if (area
> res) res = area;
}
printf("%lld\n",res);
return 0;
}